/*
 * sheet.c
 *
 *  Created on: 2016年8月7日
 *      Author: pengzhikang
 */

#include "sheet.h"

/**
 * 图层管理结构初始化函数
 */
struct SHTCTL * shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize) {
	struct SHTCTL *ctl;
	int i;
	ctl = (struct SHTCTL *) memman_alloc_4k(memman, sizeof(struct SHTCTL));
	if (ctl == 0) {
		goto err;
	}
	ctl->map = (unsigned char *) memman_alloc_4k(memman, xsize * ysize);
	if (ctl->map == 0) {
		memman_free_4k(memman, (int) ctl, sizeof(struct SHTCTL));
		goto err;
	}
	ctl->vram = vram;
	ctl->xsize = xsize;
	ctl->ysize = ysize;
	ctl->top = -1; //一个SHEET都没有
	for (i = 0; i < MAX_SHEETS; ++i) {
		ctl->sheets0[i].flags = 0; // 标记为未使用
		ctl->sheets0[i].ctl = ctl;
	}
	err: return ctl;
}

/**
 * 创建新的图层
 */
struct SHEET * sheet_alloc(struct SHTCTL *ctl) {
	struct SHEET *sht;
	int i;
	for (i = 0; i < MAX_SHEETS; ++i) {
		if (ctl->sheets0[i].flags == 0) { //寻找未使用的图层
			sht = &ctl->sheets0[i];
			sht->flags = SHEET_USE; //标记为正在使用
			sht->height = -1; //隐藏
			sht->task = 0; //自动关闭功能
			return sht;
		}
	}
	return 0; //所有的SHEET都在使用中
}

/**
 * 设置图层的缓冲区大小和透明色
 */
void sheet_setbuf(struct SHEET *sht, unsigned char *buf, int xsize, int ysize, int col_inv) {
	sht->buf = buf;
	sht->bxsize = xsize;
	sht->bysize = ysize;
	sht->col_inv = col_inv;
}

void sheet_refreshmap(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0) {
	/* 以b开头的变量定义了图形在缓冲区中的坐标 */
	/* 以v开头的变量定义了图形在屏幕上的坐标 */
	int h, bx, by, vx, vy, bx0, by0, bx1, by1, sid4, *p;
	unsigned char *buf, sid, *map = ctl->map;
	struct SHEET *sht;
	// 如果refresh的范围超出了画面则修正
	vx0 = vx0 < 0 ? 0 : vx0;
	vy0 = vy0 < 0 ? 0 : vy0;
	vx1 = vx1 > ctl->xsize ? ctl->xsize : vx1;
	vy1 = vy1 > ctl->ysize ? ctl->ysize : vy1;

	for (h = h0; h <= ctl->top; h++) {
		sht = ctl->sheets[h];
		sid = sht - ctl->sheets0; // 将进行了减法计算的地址作为图层号码使用
		buf = sht->buf;
		// 使用vx0~vy1,对bx0~by1进行倒推
		bx0 = vx0 - sht->vx0;
		by0 = vy0 - sht->vy0;
		bx1 = vx1 - sht->vx0;
		by1 = vy1 - sht->vy0;

		bx0 = bx0 < 0 ? 0 : bx0;
		by0 = by0 < 0 ? 0 : by0;
		bx1 = bx1 > sht->bxsize ? sht->bxsize : bx1;
		by1 = by1 > sht->bysize ? sht->bysize : by1;

		if (sht->col_inv == -1) { // 无透明色的
			if ((sht->vx0 & 3) == 0 && (bx0 & 3) == 0 && (bx1 & 3) == 0) {
				// 一次写4个字节
				bx1 = (bx1 - bx0) / 4;
				sid4 = sid | sid << 8 | sid << 16 | sid << 24;
				for (by = by0; by < by1; ++by) {
					vy = sht->vy0 + by;
					vx = sht->vx0 + bx0;
					p = (int *) &map[vy * ctl->xsize + vx];
					for (bx = 0; bx < bx1; ++bx) {
						p[bx] = sid4;
					}
				}
			} else {
				// 一次1个字节
				for (by = by0; by < by1; ++by) {
					vy = sht->vy0 + by;
					for (bx = bx0; bx < bx1; ++bx) {
						vx = sht->vx0 + bx;
						map[vy * ctl->xsize + vx] = sid;
					}
				}
			}
		} else { //有透明色的
			for (by = by0; by < by1; ++by) {
				vy = sht->vy0 + by;
				for (bx = bx0; bx < bx1; ++bx) {
					vx = sht->vx0 + bx;
					if (buf[by * sht->bxsize + bx] != sht->col_inv) {
						map[vy * ctl->xsize + vx] = sid;
					}
				}
			}
		}
	}
}

/**
 * 刷新部分范围的图层
 * vx0, vy1定义该矩形区域的左上角坐标
 * vx1, vy1定义该矩形区域的右下角坐标
 */
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0, int h1) {
	/* 以b开头的变量定义了图形在缓冲区中的坐标 */
	/* 以v开头的变量定义了图形在屏幕上的坐标 */
	int h, bx, by, vx, vy, bx0, by0, bx1, by1;
	unsigned char *buf, sid, *vram = ctl->vram, *map = ctl->map;
	struct SHEET *sht;
	int j, bx2, sid4, i, i1, *p, *q, *r;
	// 如果refresh的范围超出了画面则修正
	vx0 = vx0 < 0 ? 0 : vx0;
	vy0 = vy0 < 0 ? 0 : vy0;
	vx1 = vx1 > ctl->xsize ? ctl->xsize : vx1;
	vy1 = vy1 > ctl->ysize ? ctl->ysize : vy1;

	for (h = h0; h <= h1; h++) {
		sht = ctl->sheets[h];
		buf = sht->buf;
		sid = sht - ctl->sheets0;
		// 使用vx0~vy1,对bx0~by1进行倒推
		bx0 = vx0 - sht->vx0;
		by0 = vy0 - sht->vy0;
		bx1 = vx1 - sht->vx0;
		by1 = vy1 - sht->vy0;

		bx0 = bx0 < 0 ? 0 : bx0;
		by0 = by0 < 0 ? 0 : by0;
		bx1 = bx1 > sht->bxsize ? sht->bxsize : bx1;
		by1 = by1 > sht->bysize ? sht->bysize : by1;

		if ((sht->vx0 & 3) == 0) {
			// 4字节
			i = (bx0 + 3) / 4; // 小数进位
			i1 = bx1 / 4; //小数舍去
			i1 = i1 - i;
			sid4 = sid | sid << 8 | sid << 16 | sid << 24;
			for (by = by0; by < by1; ++by) {
				vy = sht->vy0 + by;
				// 前面被4除多余的部分逐个字节写入
				for (bx = bx0; bx < bx1 && (bx & 3) != 0; ++bx) {
					vx = sht->vx0 + bx;
					if (map[vy * ctl->xsize + vx] == sid) {
						vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];
					}
				}
				vx = sht->vx0 + bx;
				p = (int *) &map[vy * ctl->xsize + vx];
				q = (int *) &vram[vy * ctl->xsize + vx];
				r = (int *) &buf[by * sht->bxsize + bx];
				// 4的倍数部分
				for (i = 0; i < i1; i++) {
					if (p[i] == sid4) {
						q[i] = r[i];
					} else {
						bx2 = bx + i * 4;
						vx = sht->vx0 + bx2;
						for (j = 0; j < 4; ++j) {
							if (map[vy * ctl->xsize + vx + j] == sid) {
								vram[vy * ctl->xsize + vx + j] = buf[by * sht->bxsize + bx2 + j];
							}
						}
					}
				}
				for (bx += i1 * 4; bx < bx1; bx++) {
					vx = sht->vx0 + bx;
					if (map[vy * ctl->xsize + vx] == sid) {
						vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];
					}
				}
			}
		} else {
			for (by = by0; by < by1; ++by) {
				vy = sht->vy0 + by;
				for (bx = bx0; bx < bx1; ++bx) {
					vx = sht->vx0 + bx;
					if (map[vy * ctl->xsize + vx] == sid) {
						vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];
					}
				}
			}
		}

	}
}

/**
 * 设定底板高度
 */
void sheet_updown(struct SHEET *sht, int height) {
	struct SHTCTL *ctl = sht->ctl;
	int h, old = sht->height; //存储设置前的高度信息

	if (height > ctl->top + 1) {
		height = ctl->top + 1;
	}
	if (height < -1) {
		height = -1;
	}
	sht->height = height; // 设定高度

	// sheet[]重排
	if (old > height) { //比以前低
		if (height >= 0) {
			// 把中间的往上提
			for (h = old; h > height; h--) {
				ctl->sheets[h] = ctl->sheets[h - 1];
				ctl->sheets[h]->height = h;
			}
			ctl->sheets[height] = sht;
			//按照新图层的信息重新绘制画面
			sheet_refreshmap(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, height + 1);
			sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, height + 1, old);
		} else { //隐藏
			if (ctl->top > old) {
				// 把上面的降下来
				for (h = old; h < ctl->top; h++) {
					ctl->sheets[h] = ctl->sheets[h + 1];
					ctl->sheets[h]->height = h;
				}
			}
			ctl->top--; //由于显示的图层少了一个，所以最上面的图层高度下降
			//按照新图层的信息重新绘制画面
			sheet_refreshmap(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, 0);
			sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, 0, old - 1);
		}
	} else if (old < height) { //比以前高
		if (old >= 0) {
			// 把中间的拉上来
			for (h = old; h < height; h++) {
				ctl->sheets[h] = ctl->sheets[h + 1];
				ctl->sheets[h]->height = h;
			}
			ctl->sheets[height] = sht;
		} else { //由隐藏状态转为显示状态
			// 将已在上面的提上来
			for (h = ctl->top; h >= height; h--) {
				ctl->sheets[h + 1] = ctl->sheets[h];
				ctl->sheets[h + 1]->height = h + 1;
			}
			ctl->sheets[height] = sht;
			ctl->top++;			//由于已显示图层增加了一个，所以最上面的图层高度增加
		}
		//按照新图层的信息重新绘制画面
		sheet_refreshmap(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, height);
		sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, height, height);
	}
}

/**
 * 刷新部分范围的图层
 * bx0, by1定义该矩形区域的左上角坐标
 * bx1, by1定义该矩形区域的右下角坐标
 */
void sheet_refresh(struct SHEET *sht, int bx0, int by0, int bx1, int by1) {
	if (sht->height >= 0) {/* 如果该图层不是一个隐藏图层 */
		/* 计算出实际坐标并调用sheet_refreshsub对该区域进行重画 */
		sheet_refreshsub(sht->ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1, sht->height, sht->height);
	}
}

/**
 * 刷新整个图层
 */
void sheet_all_refresh(struct SHTCTL *ctl) {
	int h, bx, by, vx, vy;
	unsigned char *buf, c, *vram = ctl->vram;
	struct SHEET *sht;
	for (h = 0; h <= ctl->top; h++) {
		sht = ctl->sheets[h];
		buf = sht->buf;
		for (by = 0; by < sht->bysize; by++) {
			vy = sht->vy0 + by;
			for (bx = 0; bx < sht->bxsize; bx++) {
				vx = sht->vx0 + bx;
				c = buf[by * sht->bxsize + bx];
				if (c != sht->col_inv) {
					vram[vy * ctl->xsize + vx] = c;
				}
			}
		}
	}
}

/**
 * 上下左右移动图层
 */
void sheet_slide(struct SHEET *sht, int vx0, int vy0) {
	struct SHTCTL *ctl = sht->ctl;
	int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
	sht->vx0 = vx0;
	sht->vy0 = vy0;
	if (sht->height >= 0) {/* 如果该图层不是一个隐藏图层 */
		sheet_refreshmap(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0);
		sheet_refreshmap(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height);

		sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0, sht->height - 1);
		sheet_refreshsub(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height, sht->height);
	}
}

/**
 * 释放已使用的图层
 */
void sheet_free(struct SHEET *sht) {
	if (sht->height >= 0) {
		sheet_updown(sht, -1);
	}
	sht->flags = SHEET_NOT_USE;
}
